#! /usr/bin/env python3
#
# This script (maximize-window.py) maximizes a GUI window.  See README.md for explanations.
#
# Copyright 2022 陈睿超 (Chen, Ruichao) <sheep0x.moe@protonmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.






import xcffib
import xcffib.xproto      # xcffib quirk:  Must import `xcffib.xproto` even if you don't use it.  Required for internal stuffs.
import os
import sys


# FIXME It's been a while since I used Python.  Is this the correct way to write context managers?
class ConnectionContext:
	def __enter__(self):
		conn = self.conn = xcffib.Connection(os.environ['DISPLAY'])
		conn.invalid()
		return conn
	def __exit__(self, _a, _b, _c):
		# NOTE WMs may ignore our message if we disconnect() too early, causing
		#      a Heisenbug.  Maximization may always work, always fail, or work
		#      50% of the time.  There are two workarounds:
		#      1.  time.sleep(0.01) before quitting
		#      2.  quit without disconnect()'ing
		#      If the 2nd method doesn't work for you, try using the 1st method.

		# method 1:
		#import time
		#time.sleep(0.01)
		#self.conn.disconnect()

		# method 2:
		pass #self.conn.disconnect()




with ConnectionContext() as conn:
	xproto   = xcffib.xproto.xprotoExtension(conn)          # FIXME confusing names, poor readability (xproto instance vs xproto module)
	wid      = int(sys.argv[1])  # or throw
	root_wid = xproto.QueryTree(wid).reply().root

	def intern(name):
		return xproto.InternAtom(False, len(name), name).reply().atom
	atom__NET_WM_STATE = intern('_NET_WM_STATE')
	atom_vMaximize = intern('_NET_WM_STATE_MAXIMIZED_VERT')
	atom_hMaximize = intern('_NET_WM_STATE_MAXIMIZED_HORZ')


	def EWMH_send_msg_to_root(*, type, window, format, data):
		from xcffib.xproto import EventMask

		# construct msg
		assert format == 32                  # each datum can be 1/2/4 bytes.  We only support 4 bytes atm.
		assert format * len(data) == 8 * 20  # 20 bytes total
		xcffib_data = xcffib.xproto.ClientMessageData .synthetic(data, 'I' * 5)
		xcffib_msg  = xcffib.xproto.ClientMessageEvent.synthetic(format=32, window=window, type=type, data=xcffib_data)
		bin_msg = xcffib_msg.pack()

		# wrap msg in request. send.
		propagate  = False
		dest       = root_wid
		event_mask = EventMask.SubstructureNotify | EventMask.SubstructureRedirect
		event      = bin_msg
		xproto.SendEvent(propagate, dest, event_mask, event)   # no reply(??)
		conn.flush()


	# maximize window
	#_NET_WM_STATE_REMOVE = 0
	_NET_WM_STATE_ADD    = 1
	#_NET_WM_STATE_TOGGLE = 2
	EWMH_send_msg_to_root(type=atom__NET_WM_STATE, window=wid, format=32, data=[
		_NET_WM_STATE_ADD,        # action on property
		atom_vMaximize,           # 1st property
		atom_hMaximize,           # 2nd property (0 if only 1 property)
		0,                        # source indication      NOTE technically this shouldn't be 0 but we are lazy and EWMH allows legacy code to use 0.
		0,                        # padding
	])
